﻿using System.IO;
namespace ExtendedStream
{
    public static class StreamExtentionMethod
    {
        /// <summary>
        /// Read until require count be reached or end of Stream.
        /// </summary>
        /// <param name="stream">this stream</param>
        /// <param name="buffer">An array of bytes. When this method returns, the buffer contains the specified byte array with the values between <paramref name="offset"/> and (<paramref name="offset"/> + <paramref name="count"/> - 1) replaced by the bytes read from the current source.
        /// </param><param name="offset">The zero-based byte offset in <paramref name="buffer"/> at which to begin storing the data read from the current stream.
        /// </param><param name="count">The maximum number of bytes to be read from the current stream.
        /// </param>
        /// <returns>The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.</returns>
        public static int SafeRead(this Stream stream,byte[] buffer,int offset,int count)
        {
            int result=0;
            while (result < count)
            {
                int readSize = stream.Read(buffer, offset + result, count-result);
                if (readSize == 0)
                    return result;
                result += readSize;
                
            }
            return result;
        }

        /// <summary>
        /// Add byte array header to this stream
        /// </summary>
        /// <param name="stream">this stream (Attention this stream will be closed when returned stream be closed.)</param>
        /// <param name="buffer">header buffer</param>
        /// <param name="offset">start index in buffer</param>
        /// <param name="count">buffer use size</param>
        /// <returns>new Headered Stream</returns>
        public static Stream AddHeader(this Stream stream, byte[] buffer, int offset, int count)
        {
            return new MergedStream(new MemoryStream(buffer, offset, count), stream);
        }

        /// <summary>
        /// Add stream header to this stream
        /// </summary>
        /// <param name="stream">this stream (Attention this stream will be closed when returned stream be closed.)</param>
        /// <param name="header">header stream</param>
        /// <returns>new Headered Stream</returns>
        public static Stream AddHeader(this Stream stream, Stream header)
        {
            return new MergedStream(header, stream);
        }

        /// <summary>
        /// Add byte array Tail to end of this stream
        /// </summary>
        /// <param name="stream">this stream (Attention this stream will be closed when returned stream be closed.)</param>
        /// <param name="buffer">Tail buffer</param>
        /// <param name="offset">start index in buffer</param>
        /// <param name="count">buffer use size</param>
        /// <returns>new Tailed Stream</returns>
        public static Stream AddTail(this Stream stream, byte[] buffer, int offset, int count)
        {
            return new MergedStream(stream, new MemoryStream(buffer, offset, count));
        }

        /// <summary>
        /// Add stream tail to this stream
        /// </summary>
        /// <param name="stream">this stream (Attention this stream will be closed when returned stream be closed.)</param>
        /// <param name="tail">tail stream</param>
        /// <returns>new tailed Stream</returns>
        public static Stream AddTail(this Stream stream, Stream tail)
        {
            return new MergedStream(stream,tail);
        }

        /// <summary>
        /// Create sub stream of this stream
        /// </summary>
        /// <param name="stream">this stream (Attention this stream will be closed when returned stream be closed.)</param>
        /// <param name="offset">new stream start index</param>
        /// <param name="count">new stream Lenght</param>
        /// <param name="seekToOffset">seek to offset on startup(true by default)</param>
        /// <returns></returns>
        public static Stream SubStream(this Stream stream, long offset, long count,bool seekToOffset=true)
        {
            return new SubStream(stream,offset,count,seekToOffset);
        }

        /// <summary>
        /// Create sub stream of this stream
        /// </summary>
        /// <param name="stream">this stream (Attention this stream will be closed when returned stream be closed.)</param>
        /// <param name="offset">new stream start index</param>
        /// <param name="seekToOffset">seek to offset on startup(true by default)</param>
        /// <returns></returns>
        public static Stream SubStream(this Stream stream, long offset,  bool seekToOffset = true)
        {
            return new SubStream(stream, offset, seekToOffset);
        }
    }
}
